#pragma once
// 实现语音识别子服务
/*
    1.包含语音识别客户端
    2.包含brpc服务器，其中实现SpeechServiceImpl为调用语音客户端对语音信息进行识别
    3.服务器注册客户端etcd
*/
#include <brpc/server.h>
#include <butil/logging.h>
#include "logger.hpp"                    //日志模块封装
#include "etcd.hpp"                      //服务注册模块封装
#include "data_es.hpp"                   //es数据管理客户端的封装
#include "mysql_chat_session.hpp"        //mysql数据管理客户端的封装
#include "mysql_chat_session_member.hpp" //mysql数据管理客户端的封装
#include "mysql_apply.hpp"               //mysql数据管理客户端的封装
#include "mysql_relation.hpp"            //mysql数据管理客户端的封装
#include "friend.pb.h"                   //protobuf框架代码
#include "base.pb.h"
#include "user.pb.h"    //文件的上传和下载
#include "message.pb.h" //消息的存储
#include "utils.hpp"
#include "channel.hpp"

/*
    用户管理子模块相比文件管理子模块多了信道管理对象、服务发现对象、文件子服务名称
    因为头像是存储在文件系统的，通过文件服务名称在信道管理对象找到文件子服务的信道
    调用文件子服务进行通信，获取头像

    服务发现对象没用到

    ServiceManager::ptr _mm_channel; //RPC信道管理对象,进行文件子服务的rpc，请求进行头像的文件下载
    std::string _file_name_service; //文件子服务的名称
    Discovery::ptr _service_discoverer;                 //服务发现对象
*/

namespace zrb
{
    // （1）.创建rpc服务子类继承pb中的EchoService服务类，并实现内部的业务接口逻辑
    class FriendServiceImpl : public zrb::FriendService
    {
    public:
        FriendServiceImpl(std::shared_ptr<odb::core::database> _mysql_odb,
            std::shared_ptr<elasticlient::Client> _es_client,
            const ServiceManager::ptr& mm_channel,
            const std::string& user_name_service,
            const std::string& message_name_service)
            : _es_handle(std::make_shared<ESUser>(_es_client))
            ,_mysql_apply(std::make_shared<FriendApplyTable>(_mysql_odb))
            ,_mysql_chat_session(std::make_shared<ChatSessionTable>(_mysql_odb))
            ,_mysql_chat_session_member(std::make_shared<ChatSessionMemberTable>(_mysql_odb))
            ,_mysql_relation(std::make_shared<RelationTable>(_mysql_odb))
            , _mm_channel(mm_channel)
            ,_user_name_service(user_name_service)
            ,_message_name_service(message_name_service)
        {
        }

        ~FriendServiceImpl()
        {
        }

        virtual void GetFriendList(google::protobuf::RpcController *controller,
                                   const ::zrb::GetFriendListReq *request,
                                   ::zrb::GetFriendListRsp *response,
                                   ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            // 1. 获取请求中的用户 ID
            const std::string rid = request->request_id();
            const std::string uid = request->user_id();
            // 2. 根据用户 ID，从数据库的好友关系表&用户表中取出该用户所有的好友简息
            auto friend_id_lists = _mysql_relation->friends(uid);
            std::unordered_set<std::string> user_id_lists;
            for (auto &id : friend_id_lists)
            {
                user_id_lists.insert(id);
            }
            // 3. 根据好友简息中的好友头像 ID，批量获取头像数据，组织完整用户信息结构
            std::unordered_map<std::string, UserInfo> user_list;
            bool ret = GetUserInfo(rid, user_id_lists, user_list);
            if (ret == false)
            {
                LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                return err_response(rid, "批量获取用户信息失败!");
            }
            // 4. 组织响应，将好友列表返回给网关
            response->set_request_id(rid);
            response->set_success(true);
            for (const auto &user_it : user_list)
            {
                auto user_info = response->add_friend_list();
                user_info->CopyFrom(user_it.second);
            }
        }

        virtual void FriendRemove(google::protobuf::RpcController *controller,
                                  const ::zrb::FriendRemoveReq *request,
                                  ::zrb::FriendRemoveRsp *response,
                                  ::google::protobuf::Closure *done)

        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            // 1. 提取关键要素：当前用户ID，要删除的好友ID
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            std::string pid = request->peer_id();
            // 2. 从好友关系表中删除好友关系信息
            bool ret = _mysql_relation->remove(uid, pid);
            if (ret == false)
            {
                LOG_ERROR("{} - 从数据库删除好友信息失败！", rid);
                return err_response(rid, "从数据库删除好友信息失败！");
            }
            // 3. 从会话信息表中，删除对应的聊天会话 -- 同时删除会话成员表中的成员信息
            ret = _mysql_chat_session->remove(uid, pid);
            if (ret == false)
            {
                LOG_ERROR("{}- 从数据库删除好友会话信息失败！", rid);
                return err_response(rid, "从数据库删除好友会话信息失败！");
            }
            // 4. 组织响应
            response->set_request_id(rid);
            response->set_success(true);
        }

        virtual void FriendAdd(google::protobuf::RpcController *controller,
                               const ::zrb::FriendAddReq *request,
                               ::zrb::FriendAddRsp *response,
                               ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            // 1. 取出请求中的请求者 ID，和被请求者 ID
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            std::string pid = request->respondent_id();
            // 2. 判断两人是否已经是好友
            bool ret = _mysql_relation->exist(uid, pid);
            if (ret == true)
            {
                LOG_ERROR("{}- 申请好友失败-两者{}-{}已经是好友关系", rid, uid, pid);
                return err_response(rid, "两者已经是好友关系！");
            }
            // 3. 判断该用户是否已经申请过好友关系
            ret = _mysql_apply->exists(uid, pid);
            if (ret == true)
            {
                LOG_ERROR("{}- 申请好友失败-已经申请过对方好友！", rid, uid, pid);
                return err_response(rid, "已经申请过对方好友！");
            }
            // 4. 向好友申请事件表中，新增申请信息
            std::string eid = uuid();
            Friend_Apply ev(eid, uid, pid);
            ret = _mysql_apply->insert(ev);
            if (ret == false)
            {
                LOG_ERROR("{} - 向数据库新增好友申请事件失败！", rid);
                return err_response(rid, "向数据库新增好友申请事件失败！");
            }
            // 5. 组织响应，将事件 ID 信息响应给网关
            response->set_request_id(rid);
            response->set_success(true);
            response->set_notify_event_id(eid);
        }

        virtual void FriendAddProcess(google::protobuf::RpcController *controller,
                                      const ::zrb::FriendAddProcessReq *request,
                                      ::zrb::FriendAddProcessRsp *response,
                                      ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            // 1. 取出请求中的申请人 ID，和被申请人 ID，以及处理结果
            std::string rid = request->request_id();
            std::string eid = request->notify_event_id();
            std::string uid = request->user_id();       // 被申请人
            std::string pid = request->apply_user_id(); // 申请人
            bool agree = request->agree();
            // 2. 根据两人 ID 在申请事件表中查询判断是否存在申请事件
            bool ret = _mysql_apply->exists(pid, uid);
            if (ret == false)
            {
                LOG_ERROR("{}- 没有找到{}-{}对应的好友申请事件！", rid, pid, uid);
                return err_response(rid, "没有找到对应的好友申请事件!");
            }
            // 3. 判断两人是否已经是好友（互相加好友的情况）
            ret = _mysql_apply->exists(uid, pid);
            if (ret == true)
            {
                LOG_ERROR("{}- 申请好友失败-已经申请过对方好友！", rid, uid, pid);
                return err_response(rid, "已经申请过对方好友！");
            }
            // 4. 不管拒绝还是同意，删除申请事件表中的事件信息（该事件处理完毕）
            ret = _mysql_apply->remove(pid, uid);
            if (ret == false)
            {
                LOG_ERROR("{}- 从数据库删除申请事件 {}-{} 失败！", rid, pid, uid);
                return err_response(rid, "从数据库删除申请事件失败!");
            }
            // 5. 若同意申请，则向用户关系表中添加好友关系数据，向会话表中新增会话信息，向会话成员表中新增成员信息
            std::string cssid;
            if (agree == true)
            {
                ret = _mysql_relation->insert(uid, pid);
                if (ret == false)
                {
                    LOG_ERROR("{}- 新增好友关系信息-{}-{}！", rid, uid, pid);
                    return err_response(rid, "新增好友关系信息!");
                }
                cssid = uuid();
                Chat_Session cs(cssid, "", ChatSessionType::SINGLE);
                ret = _mysql_chat_session->insert(cs);
                if (ret == false)
                {
                    LOG_ERROR("{}- 新增单聊会话信息-{}！", rid, cssid);
                    return err_response(rid, "新增单聊会话信息失败!");
                }
                Session_member csm1(cssid, uid);
                Session_member csm2(cssid, pid);
                std::vector<Session_member> mlist = {csm1, csm2};
                ret = _mysql_chat_session_member->append(mlist);
                if (ret == false)
                {
                    LOG_ERROR("{}- 没有找到{}-{}对应的好友申请事件！", rid, pid, uid);
                    return err_response(rid, "没有找到对应的好友申请事件!");
                }
            }
            // 6. 组织响应，将新生成的会话 ID 响应给网关
            response->set_request_id(rid);
            response->set_success(true);
            response->set_new_session_id(cssid);
        }

        virtual void FriendSearch(google::protobuf::RpcController *controller,
                                  const ::zrb::FriendSearchReq *request,
                                  ::zrb::FriendSearchRsp *response,
                                  ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            // 1. 取出请求中的用户 ID，和搜索关键字
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            std::string skey = request->search_key();
            LOG_DEBUG("{} 好友搜索 ： {}", uid, skey);
            // 2. 从好友关系表中取出该用户所有好友 ID
            auto friend_id_lists = _mysql_relation->friends(uid);
            // 3. 根据关键字从 ES 服务器中进行用户搜索，搜索的时候需要将关键字作为用户 ID/手机号/昵称的搜索关键字进行搜索，且需要根据自己的 ID 和好友 ID 过滤掉自己和自己的好友。
            std::unordered_set<std::string> user_id_lists;
            friend_id_lists.push_back(uid); // 把自己也过滤掉
            auto search_res = _es_handle->search(skey, friend_id_lists);
            for (auto &it : search_res)
            {
                user_id_lists.insert(it.user_id());
            }
            // 4. 根据搜索到的用户简息中的头像 ID，从文件服务器批量获取用户头像数据
            std::unordered_map<std::string, UserInfo> user_list;
            bool ret = GetUserInfo(rid, user_id_lists, user_list);
            if (ret == false)
            {
                LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                return err_response(rid, "批量获取用户信息失败!");
            }
            // 5. 组织响应，将搜索到的用户列表响应给网关
            response->set_request_id(rid);
            response->set_success(true);
            for (const auto &user_it : user_list)
            {
                auto user_info = response->add_user_info();
                user_info->CopyFrom(user_it.second);
            }
        }

        virtual void GetChatSessionList(google::protobuf::RpcController *controller,
                                        const ::zrb::GetChatSessionListReq *request,
                                        ::zrb::GetChatSessionListRsp *response,
                                        ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            // 获取聊天会话的作用：一个用户登录成功后，能够展示自己的历史聊天信息
            // 1. 提取请求中的关键要素：当前请求用户ID
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            // 2. 从数据库中查询出用户的单聊会话列表
            auto sf_list = _mysql_chat_session->singleChatSession(uid);
            //  1. 从单聊会话列表中，取出所有的好友ID，从用户子服务获取用户信息
            std::unordered_set<std::string> users_id_list;
            for (const auto &f : sf_list)
            {
                users_id_list.insert(f.friend_id);
            }
            std::unordered_map<std::string, UserInfo> user_list;
            bool ret = GetUserInfo(rid, users_id_list, user_list);
            if (ret == false)
            {
                LOG_ERROR("{} - 批量获取用户信息失败！", rid);
                return err_response(rid, "批量获取用户信息失败!");
            }
            // 2. 设置响应会话信息：会话名称就是好友名称；会话头像就是好友头像
            // 3. 从数据库中查询出用户的群聊会话列表
            auto gc_list = _mysql_chat_session->groupChatSession(uid);

            // 4. 根据所有的会话ID，从消息存储子服务获取会话最后一条消息
            // 5. 组织响应
            for (const auto &f : sf_list)
            {
                auto chat_session_info = response->add_chat_session_info_list();
                chat_session_info->set_single_chat_friend_id(f.friend_id);
                chat_session_info->set_chat_session_id(f.chat_session_id);
                chat_session_info->set_chat_session_name(user_list[f.friend_id].nickname());
                chat_session_info->set_avatar(user_list[f.friend_id].avatar());
                MessageInfo msg;
                ret = GetRecentMsg(rid, f.chat_session_id, msg);
                if (ret == false)
                {
                    continue;
                }
                chat_session_info->mutable_prev_message()->CopyFrom(msg);
            }
            for (const auto &f : gc_list)
            {
                auto chat_session_info = response->add_chat_session_info_list();
                chat_session_info->set_chat_session_id(f.chat_session_id);
                chat_session_info->set_chat_session_name(f.chat_session_name);
                MessageInfo msg;
                ret = GetRecentMsg(rid, f.chat_session_id, msg);
                if (ret == false)
                {
                    continue;
                }
                chat_session_info->mutable_prev_message()->CopyFrom(msg);
            }
            response->set_request_id(rid);
            response->set_success(true);
        }

        virtual void ChatSessionCreate(google::protobuf::RpcController *controller,
                                       const ::zrb::ChatSessionCreateReq *request,
                                       ::zrb::ChatSessionCreateRsp *response,
                                       ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };

            // 创建会话，其实针对的是用户要创建一个群聊会话
            // 1. 提取请求关键要素：会话名称，会话成员
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            std::string cssname = request->chat_session_name();

            // 2. 生成会话ID，向数据库添加会话信息，添加会话成员信息
            std::string cssid = uuid();
            Chat_Session cs(cssid, cssname, ChatSessionType::GROUP);
            bool ret = _mysql_chat_session->insert(cs);
            if (ret == false)
            {
                LOG_ERROR("{} - 向数据库添加会话信息失败: {}", rid, cssname);
                return err_response(rid, "向数据库添加会话信息失败!");
            }
            std::vector<Session_member> member_list;
            for (int i = 0; i < request->member_id_list_size(); i++)
            {
                Session_member csm(cssid, request->member_id_list(i));
                member_list.push_back(csm);
            }
            ret = _mysql_chat_session_member->append(member_list);
            if (ret == false)
            {
                LOG_ERROR("{} - 向数据库添加会话成员信息失败: {}", rid, cssname);
                return err_response(rid, "向数据库添加会话成员信息失败!");
            }
            // 3. 组织响应---组织会话信息
            response->set_request_id(rid);
            response->set_success(true);
            response->mutable_chat_session_info()->set_chat_session_id(cssid);
            response->mutable_chat_session_info()->set_chat_session_name(cssname);
        }

        virtual void GetChatSessionMember(google::protobuf::RpcController *controller,
                                          const ::zrb::GetChatSessionMemberReq *request,
                                          ::zrb::GetChatSessionMemberRsp *response,
                                          ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            // 用于用户查看群聊成员信息的时候：进行成员信息展示
            // 1. 提取关键要素：聊天会话ID
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            std::string cssid = request->chat_session_id();
            // 2. 从数据库获取会话成员ID列表
            auto member_id_lists = _mysql_chat_session_member->member(cssid);
            std::unordered_set<std::string> uid_list;
            for (const auto &id : member_id_lists)
            {
                uid_list.insert(id);
            }
            // 3. 从用户子服务批量获取用户信息
            std::unordered_map<std::string, UserInfo> user_list;
            bool ret = GetUserInfo(rid, uid_list, user_list);
            if (ret == false)
            {
                LOG_ERROR("{} - 从用户子服务获取用户信息失败！", rid);
                return err_response(rid, "从用户子服务获取用户信息失败!");
            }
            // 4. 组织响应
            response->set_request_id(rid);
            response->set_success(true);
            for (const auto &uit : user_list)
            {
                auto user_info = response->add_member_info_list();
                user_info->CopyFrom(uit.second);
            }
        }

        virtual void GetPendingFriendEventList(google::protobuf::RpcController *controller,
                                               const ::zrb::GetPendingFriendEventListReq *request,
                                               ::zrb::GetPendingFriendEventListRsp *response,
                                               ::google::protobuf::Closure *done)
        {
            brpc::ClosureGuard rpc_guard(done);
            // 定义一个错误回调函数
            auto err_response = [this, response](const std::string &id, const std::string &errmsg)
            {
                response->set_request_id(id);
                response->set_success(false);
                response->set_errmsg(errmsg);
                return;
            };
            // 1. 取出请求中的用户 ID
            std::string rid = request->request_id();
            std::string uid = request->user_id();
            // 2. 根据用户 ID，从申请事件表&用户表中找到该用户所有状态为 PENDING 的待处理事件关联申请人用户简息
            auto res = _mysql_apply->apply_users(uid);
            std::unordered_set<std::string> user_id_lists;
            for (auto &id : res)
            {
                user_id_lists.insert(id);
            }
            LOG_DEBUG("用户个数为{}",user_id_lists.size());
            // 3. 根据申请人用户头像 ID，从文件存储子服务器获取所有用户头像信息，组织用户信息结构
            std::unordered_map<std::string, UserInfo> user_list;
            bool ret = GetUserInfo(rid, user_id_lists, user_list);
            if (ret == false)
            {
                LOG_ERROR("{} - 批量获取用户信息失败!", rid);
                return err_response(rid, "批量获取用户信息失败!");
            }
            // 4. 组织响应，将申请事件列表响应给网关
            response->set_request_id(rid);
            response->set_success(true);
            for (const auto &user_it : user_list)
            {
                auto ev = response->add_event();
                ev->mutable_sender()->CopyFrom(user_it.second);
            }
        }

    private:
        bool GetUserInfo(const std::string &rid,
                         const std::unordered_set<std::string> &uid_list,
                         std::unordered_map<std::string, UserInfo> &user_list)
        {
            auto channel = _mm_channel->choose(_user_name_service);
            if (!channel)
            {
                LOG_ERROR("{} - 获取用户子服务信道失败！！", rid);
                return false;
            }

            GetMultiUserInfoReq req;
            GetMultiUserInfoRsp rsp;
            req.set_request_id(rid);
            for (auto &id : uid_list)
            {
                req.add_users_id(id);
            }
            brpc::Controller cntl;
            zrb::UserService_Stub stub(channel.get());
            stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr);

            if (cntl.Failed() == true)
            {
                LOG_ERROR("{} - 用户子服务调用失败: {}", rid, cntl.ErrorText());
                return false;
            }
            if (rsp.success() == false)
            {
                LOG_ERROR("{} - 批量获取用户信息失败: {}", rid, rsp.errmsg());
                return false;
            }

            for (const auto &user_it : rsp.users_info())
            {
                user_list.insert(std::make_pair(user_it.first, user_it.second));
            }
            return true;
        }

        bool GetRecentMsg(const std::string &rid,
                          const std::string &cssid, MessageInfo &msg)
        {
            auto channel = _mm_channel->choose(_message_name_service);
            if (!channel)
            {
                LOG_ERROR("{} - 获取消息子服务信道失败！！", rid);
                return false;
            }
            GetRecentMsgReq req;
            GetRecentMsgRsp rsp;
            req.set_request_id(rid);
            req.set_chat_session_id(cssid);
            req.set_msg_count(1);
            brpc::Controller cntl;
            zrb::MsgStorageService_Stub stub(channel.get());
            stub.GetRecentMsg(&cntl, &req, &rsp, nullptr);
            if (cntl.Failed() == true)
            {
                LOG_ERROR("{} - 消息存储子服务调用失败: {}", rid, cntl.ErrorText());
                return false;
            }
            if (rsp.success() == false)
            {
                LOG_ERROR("{} - 获取会话 {} 最近消息失败: {}", rid, cssid, rsp.errmsg());
                return false;
            }
            if (rsp.msg_list_size() > 0)
            {
                msg.CopyFrom(rsp.msg_list(0));
                return true;
            }
            return false;
        }

    private:
        // odb框架和mysql操作封装句柄
        FriendApplyTable::ptr _mysql_apply;
        ChatSessionTable::ptr _mysql_chat_session;
        ChatSessionMemberTable::ptr _mysql_chat_session_member;
        RelationTable::ptr _mysql_relation;

        // es用户操作客户端
        ESUser::ptr _es_handle;

        ServiceManager::ptr _mm_channel;   // RPC信道管理对象,进行文件子服务的rpc，请求进行头像的文件下载
        std::string _user_name_service;    // 用户管理子服务的名称
        std::string _message_name_service; // 消息存储子服务的名称
    };

    // （2）.创建rpc服务器类，搭建服务器
    //  （3）.向服务器中添加rpc子服务对象 -- 告诉服务器收到什么请求用哪个接口处理
    class FriendServer
    {
    public:
        using ptr = shared_ptr<FriendServer>;
        FriendServer(const Registry::ptr &service_registry,
                     const Discovery::ptr &service_discoverer,
                     const std::shared_ptr<brpc::Server> &rpc_server,
                     const std::shared_ptr<odb::core::database> &mysql_odb,
                     const std::shared_ptr<elasticlient::Client> &es_client)
            : _service_discoverer(service_discoverer), _service_registry(service_registry), _rpc_server(rpc_server), _mysql_odb(mysql_odb), _es_client(es_client)
        {
        }

        void start()
        {
            _rpc_server->RunUntilAskedToQuit(); // 休眠等待运行结束
        }

        ~FriendServer()
        {
        }

    private:
        Discovery::ptr _service_discoverer;        // 服务发现对象
        Registry::ptr _service_registry;           // etcd服务器注册类
        std::shared_ptr<brpc::Server> _rpc_server; // brpc服务器对象
        std::shared_ptr<odb::core::database> _mysql_odb;
        std::shared_ptr<elasticlient::Client> _es_client;
    };

    // SpeechServer构造过程参数太多，使用建造者模式
    class FriendServerBuild
    {
    public:
        // 构造mysql客户端
        void make_mysql_object(
            int max_pool,
            const std::string &user,
            const std::string &password,
            const std::string &dbname,
            const std::string &host,
            int port,
            const std::string &cset)
        {
            _mysql_odb = ODBFactory::create(max_pool, user, password, dbname, host, port, cset);
        }

        // 构造es客户端
        void make_es_object(std::string &host)
        {
            _es_client = ESClientFactory::create(host);
        }

        // 用于构造服务发现客户端&信道管理对象
        void make_discovery_object(const std::string &reg_host,
                                   const std::string &base_service_name,
                                   const std::string &user_service_name,
                                   const std::string &message_service_name)
        {
            _user_service_name = user_service_name;
            _message_service_name = message_service_name;
            // 信道管理对象
            _mm_channel = std::make_shared<ServiceManager>();
            _mm_channel->declared(_user_service_name);
            _mm_channel->declared(_message_service_name);
            auto put_cb = std::bind(&ServiceManager::onServiceOnline, _mm_channel.get(), std::placeholders::_1, std::placeholders::_2);
            auto del_cb = std::bind(&ServiceManager::onServiceOffline, _mm_channel.get(), std::placeholders::_1, std::placeholders::_2);
            // 2. 构造服务发现对象
            _service_discoverer = std::make_shared<Discovery>(reg_host, base_service_name, put_cb, del_cb);
        }

        // 构造etcd服务注册对象
        // 网络Ip、注册的服务名称、注册的服务地址
        void make_reg_object(const std::string &host, const std::string &name, const std::string &access_host)
        {
            _service_registry = std::make_shared<Registry>(host);
            _service_registry->registry(name, access_host); // 服务注册
        }
        // 构造brpc服务对象
        // 端口 超时时间 线程数量
        void make_rpc_object(uint16_t port,
                             uint32_t timeout,
                             uint8_t thread_num)
        {
            _rpc_server = std::make_shared<brpc::Server>();

            // 向服务器增加一个UserServerImpl服务，SpeechServiceImpl服务是自己实现的
            FriendServiceImpl *echo_service = new FriendServiceImpl(_mysql_odb, _es_client, _mm_channel, _user_service_name, _message_service_name);
            // brpc::ServiceOwnership::SERVER_DOESNT_OWN_SERVICE 表示添加服务失败，服务器也不会删除服务对象
            // brpc::ServiceOwnership::SERVER_OWNS_SERVICE 表示添加服务失败时，服务器负责删除服务对象
            int ret = _rpc_server->AddService(echo_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE);
            if (ret == -1)
            {
                LOG_ERROR("添加Rpc服务失败！");
                abort();
            }

            // 启动服务器
            brpc::ServerOptions options;
            options.idle_timeout_sec = timeout;       // 连接空闲超时时间-超时后连接被关闭
            options.num_threads = thread_num;         // io线程数量
            ret = _rpc_server->Start(port, &options); // 监听的端口
            if (ret == -1)
            {
                LOG_ERROR("rpc服务器启动失败");
                abort();
            }
        }

        FriendServer::ptr build()
        {
            if (!_mysql_odb)
            {
                LOG_ERROR("还未初始化mysql数据库模块");
                abort();
            }
            if (!_es_client)
            {
                LOG_ERROR("还未初始化es搜索引擎模块");
                abort();
            }
            if (!_mm_channel)
            {
                LOG_ERROR("还未初始化信道管理模块");
                abort();
            }
            if (!_service_discoverer)
            {
                LOG_ERROR("还未初始化服务发现模块");
                abort();
            }
            if (!_rpc_server)
            {
                LOG_ERROR("还未初始化rpc服务模块");
                abort();
            }
            if (!_service_registry)
            {
                LOG_ERROR("还未初始化服务注册客户端模块");
                abort();
            }

            FriendServer::ptr server = std::make_shared<FriendServer>(_service_registry,
                                                                    _service_discoverer,
                                                                    _rpc_server,
                                                                    _mysql_odb,
                                                                    _es_client);
            return server;
        }

    private:
        std::string _user_service_name;            // 用户管理子服务名称
        std::string _message_service_name;         // 消息存储子服务名称
        ServiceManager::ptr _mm_channel;           // RPC信道管理对象
        Discovery::ptr _service_discoverer;        // 服务发现对象
        Registry::ptr _service_registry;           // etcd服务器注册类
        std::shared_ptr<brpc::Server> _rpc_server; // brpc服务器对象
        std::shared_ptr<odb::core::database> _mysql_odb;
        std::shared_ptr<elasticlient::Client> _es_client;
    };
};
